package cn.coderliu.common;

import com.alibaba.fastjson.JSON;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import io.micrometer.core.instrument.util.IOUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.security.oauth2.common.OAuth2AccessToken;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;


@Slf4j
public class RequestFilter extends ZuulFilter {



    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

    @Autowired
    private RedisUtil redisUtil;
    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * 执行顺序
     *
     * @return
     */
    @Override
    public int filterOrder() {
        return 0;
    }

    /**
     * 过滤器是否执行
     *
     * @return
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {

        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest req = ctx.getRequest();

        log.info("**************************请求开始**************************");
        log.info("REQUEST:: " + req.getScheme() + " " + req.getRemoteAddr() + ":" + req.getRemotePort());
        log.info("REQUEST:: " + req.getScheme() + " " + req.getRemoteAddr() + ":" + req.getRemotePort());
        StringBuilder params = new StringBuilder("?");
        Enumeration<String> names = req.getParameterNames();
        if (req.getMethod().equals("GET")) {
            while (names.hasMoreElements()) {
                String name =  names.nextElement();
                params.append(name);
                params.append("=");
                params.append(req.getParameter(name));
                params.append("&");
            }
        }
        if (params.length() > 0) {
            params.delete(params.length() - 1, params.length());
        }
        log.info("REQUEST:: > " + req.getMethod() + " " + req.getRequestURI() + params + " " + req.getProtocol());
        Enumeration<String> headers = req.getHeaderNames();
        boolean multipart = false;
        while (headers.hasMoreElements()) {
            String name = headers.nextElement();
            String value = req.getHeader(name);
            if (value.startsWith("multipart/form-data")) {
                multipart = true;
            }
            log.info("REQUEST:: > " + name + ":" + value);
        }
        if (!ctx.isChunkedRequestBody()) {
            ServletInputStream inp = null;
            try {
                inp = ctx.getRequest().getInputStream();
                String body = null;
                if (inp != null) {
                    body = IOUtils.toString(inp);
                    if (multipart) {
                        log.info("REQUEST:: > " + "REQUESTBODY IS INPUTSTREAM");
                    } else {
                        log.info("REQUEST:: > " + body);
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        log.info("**************************请求结束**************************");

        String token = req.getHeader("Authorization");
        if (!StringUtils.isEmpty(token)) {
            OAuth2AccessToken tokenFromRedis = getTokenFromRedis(token);
            if(tokenFromRedis == null) {
                dealWithToken(ctx,"无效的token");
            } else if(tokenFromRedis.isExpired()) {
                dealWithToken(ctx,"token过期");
            } else {
                ctx.setSendZuulResponse(true); //将请求往后转发
                ctx.setResponseStatusCode(200);
            }
        } else {
            dealWithToken(ctx,"token为空");
        }

        return null;
    }



    private void dealWithToken(RequestContext ctx,String msg){
        HttpServletResponse response = ctx.getResponse();
        response.setHeader("Content-Type", "application/json;charset=UTF-8");
        ctx.setSendZuulResponse(false); //终止转发，返回响应报文
        ctx.setResponseStatusCode(400);
        Map<String,String> responseMap=new HashMap<String,String>();
        responseMap.put("errorcode", "400");
        responseMap.put("errormsg", msg);
        ctx.setResponseBody(JSON.toJSONString(responseMap));
    }


    private OAuth2AccessToken getTokenFromRedis(String tokenValue){
        StringRedisSerializer STRING_SERIALIZER = new StringRedisSerializer();
        if (tokenValue.contains("%20")) {
            tokenValue = tokenValue.replace("%20", " ");
            RequestContext requestContext = RequestContext.getCurrentContext();
            requestContext.addZuulRequestHeader("Authorization", tokenValue);
        }

        String[] tokenWithPrefix = tokenValue.split(" ");
        byte[] key = STRING_SERIALIZER.serialize("access:" + tokenWithPrefix[1]);
        byte[] bytes = null;
        RedisConnection conn = redisConnectionFactory.getConnection();

        try {
            bytes = conn.get(key);
        } finally {
            conn.close();
        }
        JdkSerializationRedisSerializer OBJECT_SERIALIZER = new JdkSerializationRedisSerializer();
        OAuth2AccessToken accessToken = (OAuth2AccessToken)OBJECT_SERIALIZER.deserialize(bytes);
        return accessToken;
    };
}
